i.MX6ULL开发板uboot/kernel/rootfs移植傻瓜教程【建议收藏】

您所在的位置:网站首页 uboot menuconfig详解 i.MX6ULL开发板uboot/kernel/rootfs移植傻瓜教程【建议收藏】

i.MX6ULL开发板uboot/kernel/rootfs移植傻瓜教程【建议收藏】

#i.MX6ULL开发板uboot/kernel/rootfs移植傻瓜教程【建议收藏】| 来源: 网络整理| 查看: 265

本系列教程以「i.MX6ULL」处理器的ARM开发板为实验基础,学习记录嵌入式Linux开发的各种知识与经验,主要内容包括嵌入式Linux移植,嵌入式Linux驱动开发,嵌入式Linux应用开发等。

本系列教程将以野火的i.MX6ULL eMMC开发板为硬件基础,以「野火EBF6ULL Pro开发板教程」和「正点原子i.MX6ULL阿尔法开发板教程」为参考,进行学习实践。

uboot移植初探

1 嵌入式Linux移植概述

Linux 的移植主要包括3部分:

移植「bootloader 代码」, Linux 系统要启动就必须需要一个 bootloader 程序,也就说芯片上电以后先运行一段bootloader程序。 这段bootloader程序会先初始化DDR等外设, 然后将Linux内核从flash(NAND,NOR FLASH,SD,MMC 等)拷贝到 DDR 中,最后启动 Linux 内核。 bootloader 有很多,常用的就是 U-Boot。

bootloader 和 Linux 内核的关系就跟 PC 上的 BIOS 和 Windows 的关系一样,bootloader 就相当于 BIOS。

移植「Linux 内核」,Linux内核由一系列程序组成,包括负责响应中断的中断服务程序、负责管理多个进程从而分享处理器时间的调度程序、负责管理地址空间的内存管理程序、网络、进程间通信的系统服务程序等。内核负责管理系统的硬件设备。

移植「根文件系统(rootfs)」,Linux 中的根文件系统更像是一个文件夹或者叫做目录,在这个目录里面会有很多的子目录。根目录下和子目录中会有很多的文件,这些文件是 Linux 运行所必须的,比如库、常用的软件和命令、设备文件、配置文件等等。根文件系统里面包含了一些最常用的命令和文件。

「U-Boot、Linux kernel和rootfs」 这三者一起构成了一个完整的Linux系统,一个可以正常使用、功能完善的Linux系统。

2 实验开发板简介

本测试使用的开发板为野火的i.MX6ULL eMMC开发板

3 U-Boot简介

uboot 的全称是「Universal Boot Loader」,遵循 GPL 协议的开源软件。

uboot 是一个裸机代码,可以看作是一个裸机综合例程。现在的 uboot 已经支持「液晶屏、网络、USB」等高级功能。uboot 官网为 https://www.denx.de/wiki/U-Boot/

可以在uboot官网下载uboot源码,点击左侧 Topics 中的“Source Code”,然后点击的“FTP Server” ,进入其 FTP 服务器即可看到 uboot 源码。

但我们移植uboot时一般不会直接用 uboot 官方的源码的,官方的源码是给半导体厂商准备的,半导 体厂商会根据自家的芯片,维护自己芯片对应的uboot。

NXP(freescale)维护的的uboot地址: https://github.com/Freescale/u-boot-fslc

4 NXP uboot测试

uboot移植并不需要从零开始将 uboot 移植到我们现在所使用的开发板上。因为半导体厂商通常都会自己做一个开发板「原厂开发板」,将uboot移植到他们自己的原厂开发板上,再将这个uboot(原厂BSP 包)发布出去。

市面上的开发板,通常会参考原厂的开发板做硬件,然后在原厂提供的 BSP 包上做修改,如正点原子和野火的 I.MX6ULL 开发板参考的就 是「NXP官方的I.MX6ULL EVK开发板」做的硬件:

4.1 编译环境搭建4.1.1 交叉编译器下载

嵌入式Linux开发,程序编译通常在电脑端的Linux(如虚拟机中的Ubuntu)下进行编译,Ubuntu 自带gcc 编译器,但该编译器是针对 X86 架构的!而嵌入式Linux是ARM架构的, 所以需要一个在 X86 架构上可以编译 ARM 架构代码的 gcc编译器,即「交叉编译器」。

交叉编译器有很多,本实验使用 Linaro 出品的交叉编译器,下载地址:

https://releases.linaro.org/components/toolchain/binaries/4.9-2017.01/arm-linux-gnueabihf/

4.1.2 交叉编译器安装

在Ubuntu中创建目录:/usr/local/arm,并将下载的gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz复制到此文件中,然后「解压」,解压命令如下:

sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

解压完成以后会生成一个名为“gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf”的文件夹,这个文件夹里面就是我们的交叉编译工具链。

然后,需要将该目录「添加到环境变量」中。打开/etc/profile 以后,在最后面输入如下所示内容:

export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin

使用交叉编译器之前还需要「安装其它的库」,命令如下:

sudo apt-get install lsb-core lib32stdc++6

安装完之后,可以「查看交叉编译工具的版本号」,输入如下命令:

arm-linux-gnueabihf-gcc -v

可以看到类似如下打印

以看出当前交叉编译器的版本号为 4.9.4,说明交叉编译工具链安装成功。

4.2 编译原厂uboot

编译前还要在Ubuntu 中「安装ncurses 库」,安装命令如下:

sudo apt-get install libncurses5-dev

在Ubuntu中创建存放uboot的目录,如我的目录是:/home/xxpcb/myTest/imx6ull/uboot/nxp_uboot

然后,将「NXP(freescale)的uboot源码」复制进来,这里使用的是「正点原子」提供的NXP官方原版Uboot源码包( uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2)

然后进行解压:

tar -vxjf uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

解压后的源码文件如下:

首先看下「uboot的配置」,configs 目录下有很多跟 I.MX6UL/6ULL 有关的配置,找到与mx6ull相同的,如下图。

因为我这个开发板是emmc版本的,所有就使用这个mx6ull_14x14_evk_emmc_defconfig。

编译uboot使用下面3条指令:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distcleanmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfigmake V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

这3条命令中 :

ARCH=arm 设置目标为 arm 架构

CROSS_COMPILE 指定所使用的交叉编译器。

第1条命令相当于make distclean,目的是清除工程,一般在第一次编译的时候最好清理一下工程。

第2条指令相当于make mx6ull_14x14_evk_emmc_defconfig,用于配置 uboot,配置文件为 mx6ull_14x14_evk_emmc_defconfig。

第3条指令相当于make -j8,也就是使用8核来编译uboot。

为了方便的执行着3条指令,可以「将这些指令写成shell脚本」,比如在uboot源码目录下新建一个build.sh文件,写入如下内容:

#!/bin/bashmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distcleanmake ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfigmake V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

然后进行编译:

./build.sh

编译完成以后uboot 源码多了一些文件,其中u-boot.bin就是编译出来的 uboot二进制文件。 uboot是个裸机程序, 因此需要在其前面

加上头部(IVT、 DCD等数据)才能在I.MX6U上执行,u-boot.imx 文件就是添加头部以后的 u-boot.bin。

u-boot.imx 就是我们最终要烧写到开发板中的 uboot 镜像文件。

4.3 烧录开发板

这是的烧录开发板,实际是要「烧录到SD卡中」,然后将SD卡插入开发板,让开发板从SD卡启动(需要在开发板上设置拨码开关来选择启动方式)。

4.3.1 烧录到SD卡

「正点原子」专门编写了一个小软件用来将编译出来的.bin 文件烧写到 SD 卡中,这个软件叫做“imxdownload”

将imxdownload 复制到 Ubuntu 中的uboot源码文件夹,再使用如下指令,给予 imxdownload 可执行权限:

chmod 777 imxdownload

然后「电脑USB中插入SD卡(读卡器)」,并在虚拟机中设置usb加载(VMware或VirtualBox虚拟机需要先安装「增强功能」才能使用)

然后可以使用如下指令来查看SD卡的挂载标识符:

ls /dev/sd*

查看输出结果:

这里的/dev/sdb就是我的SD卡。

注:我第一次使用SD卡烧录时,只多出了/dev/sdb,但不知什么情况,用了几次后,再插入SD卡,就会同时多出来/dev/sdb和/dev/sdb1,但实际测 试,仍然把程序烧录到/dev/sdb也能用)。

imxdownload向SD卡烧写led.bin文件,命令格式如下:

./imxdownload u-boot.bin /dev/sdb ❝

注意不能烧写到/dev/sda或sda1设备里面!那是系统磁盘。

烧写过程会输入如下信息:

烧写的最后一行会显示烧写大小、用时和速度,比如u-boot.bin烧写到SD卡中的大小是 423KB,用时 1.7s,烧写速度是 236KB/s。

注意这个烧写速度,如果这个烧写速度在几百KB/s以下那么就是正常烧写。 如果这个烧写速度大于几十MB/s、甚至几百MB/s那么肯定是烧写失败了! 重新插拔/格式化SD卡或重启ubuntu再试。

烧写完成以后会在当前工程目录下生成一个load.imx的文件,这个文件就是软件 imxdownload 根据 NXP 官方启动方式介绍的内容, 在 bin 文件前面添加了一些数据头以后生成的。最终烧写到SD卡里面的就是这个imx文件。

4.3.2 启动开发板

烧录完之后,将「SD卡插入开发板启动」,使用「串口连接电脑」,查看uboot启动信息。 设置好串口参数(波特率115200)并打开,按键「复位开发板」。 当串口打印上出现Hit any key to stop autoboot倒计时的时候「按下 键盘上的回车键」,默认是 3 秒倒计时,在 3 秒倒计时结束以后如果没有按下回车键的话 uboot 就会使用默认参数来启动 Linux 内核了。

如果在 3 秒倒计时结束之前按下回车键,那么就会进入 uboot 的命令行模式:

解读一下这些信息的含义:

第1行是 uboot 「版本号和编译时间」:当前的 uboot 版本号是 2016.03,编译时间是 2021/7 /11/15:22:25第3、4 行是 「CPU 信息」:当前使用的 CPU 是飞思卡尔(属于NXP)的 I.MX6ULL (频率为 792MHz),此时运行在 396MHz。这颗芯片是工业级的,结温为-40°C~105°C第 5 行是「复位原因」:I.MX6ULL 芯片上有个 POR_B 引脚,将这个引脚拉低即可复位 I.MX6ULL。第 6 行是「板子名字」,“MX6ULL 14x14 EVK”即NXP原厂开发板的名字 。第 7 行提示 「I2C 准备就绪」。第 8 行提示当前板子的「DRAM(内存)」 为 512MB第 9 行提示当前有「两个MMC/SD 卡控制器」:FSL_SDHC(0)和 FSL_SDHC(1)。I.MX6ULL支持两个 MMC/SD,正点原子的 I.MX6ULL EMMC 核心板上 FSL_SDHC(0)接的 SD(TF)卡,FSL_SDHC(1)接的 EMMC。第10行是一条警告信息,先忽略。第 12、13 行是 「LCD 型号」,原厂默认的是TFT43AB (480x272)。第 14~16 是「标准输入、标准输出和标准错误」所使用的终端,这里都使用串口(serial)作为终端。第 17 、18行是「切换到emmc的第0个分区上」,因为当前的 uboot 是 emmc 版本的,也就是从 emmc 启动的。我们只是为了方便将其烧写到了 SD 卡上,但是它的“内心”还是 EMMC的。所以 uboot 启动以后会将 emmc 作为默认存储器 。第 19行是「网口信息」,提示我们当前使用的 FEC1 这个网口,I.MX6ULL 支持两个网口。第 20行提示「FEC1网卡地址没有设置」(后面我们会讲解如何在uboot 里面设置网卡地址)。第 22行提示「正常启动」, 也就是说 uboot要从emmc里面读取环境变量和参数信息启动 Linux内核了。第23行是「倒计时提示」,默认倒计时 3 秒,倒计时结束之前按下回车键就会进入 Linux 命令行模式。如果在倒计时结束以后没有按下回车键,那么 Linux 内核就会启动,Linux 内核一旦启动,uboot 就运行结束了。第23行是在倒计时 3 秒内按了回车键,符号=>表示可以继续与uboot进行「命令交互」。

看过了串口的uboot信息,再来看一下板子是实际运行情况:

由于原厂的uboot驱动的屏幕是TFT43AB (480x272),与我这里屏幕不一样,所以「屏幕没有正常显示」(现在的屏幕看起来有许多彩色的小点点),接下来,就是对uboot进行屏幕驱动的修改。

在本篇结束之前,再来研究一下uboot的串口指令。

4.4 uboot命令初探

上面说道,在uboot启动的3 秒倒计时内,串口界面如果按下了回车键,uboot就会输出符号=>,则「可以继续与uboot进行命令交互」。那可以输入哪些命令呢?

4.4.1 help命令查看所有指令

输入help或?,然后按下回车即可查看当前 uboot 所支持的命令:

=> help? - alias for 'help'base - print or set address offsetbdinfo - print Board Info structurebmode - sd1|sd2|qspi1|normal|usb|sata|ecspi1:0|ecspi1:1|ecspi1:2|ecspi1:3|esdhc1|esdhc2|esdhc3|esdhc4 [noreset]bmp - manipulate BMP image databoot - boot default, i.e., run 'bootcmd'bootd - boot default, i.e., run 'bootcmd'bootelf - Boot from an ELF image in memorybootm - boot application image from memorybootp - boot image via network using BOOTP/TFTP protocolbootvx - Boot vxWorks from an ELF imagebootz - boot Linux zImage image from memoryclocks - display clocksclrlogo - fill the boot logo area with blackcmp - memory compareconinfo - print console devices and informationcp - memory copycrc32 - checksum calculationdcache - enable or disable data cachedhcp - boot image via network using DHCP/TFTP protocoldm - Driver model low level accessecho - echo args to consoleeditenv - edit environment variableenv - environment handling commandserase - erase FLASH memoryexit - exit scriptext2load- load binary file from a Ext2 filesystemext2ls - list files in a directory (default /)ext4load- load binary file from a Ext4 filesystemext4ls - list files in a directory (default /)ext4size- determine a file's sizeext4write- create a file in the root directoryfalse - do nothing, unsuccessfullyfatinfo - print information about filesystemfatload - load binary file from a dos filesystemfatls - list files in a directory (default /)fatsize - determine a file's sizefdt - flattened device tree utility commandsflinfo - print FLASH memory informationfstype - Look up a filesystem typefuse - Fuse sub-systemgo - start application at address 'addr'gpio - query and control gpio pinshelp - print command description/usagei2c - I2C sub-systemicache - enable or disable instruction cacheiminfo - print header information for application imageimxtract- extract a part of a multi-imageitest - return true/false on integer compareload - load binary file from a filesystemloadb - load binary file over serial line (kermit mode)loads - load S-Record file over serial lineloadx - load binary file over serial line (xmodem mode)loady - load binary file over serial line (ymodem mode)loop - infinite loop on address rangels - list files in a directory (default /)md - memory displaymdio - MDIO utility commandsmii - MII utility commandsmm - memory modify (auto-incrementing address)mmc - MMC sub systemmmcinfo - display MMC infomtest - simple RAM read/write testmw - memory write (fill)nfs - boot image via network using NFS protocolnm - memory modify (constant address)ping - send ICMP ECHO_REQUEST to network hostpmic - PMICprintenv- print environment variablesprotect - enable or disable FLASH write protectionreset - Perform RESET of the CPUrun - run commands in an environment variablesave - save file to a filesystemsaveenv - save environment variables to persistent storagesetenv - set environment variablessetexpr - set environment variable as the result of eval expressionsf - SPI flash sub-systemshowvar - print local hushshell variablessize - determine a file's sizesleep - delay execution for some timesource - run script from memorytest - minimal test like /bin/shtftpboot- boot image via network using TFTP protocoltrue - do nothing, successfullyusb - USB sub-systemusbboot - boot from USB deviceversion - print monitor, compiler and linker version=>4.4.2 查看指令的使用说明

命令的具体使用方法,可以输入help 命令名或? 命令名查看,以“bootz”这个命令为例:

=> help bootzbootz - boot Linux zImage image from memoryUsage:bootz [addr [initrd[:size]] [fdt]] - boot Linux zImage stored in memory The argument 'initrd' is optional and specifies the address of the initrd in memory. The optional argument ':size' allows specifying the size of RAW initrd. When booting a Linux kernel which requires a flat device-tree a third argument is required which is the address of the device-tree blob. To boot that kernel without an initrd image, use a '-' for the second argument. If you do not pass a third a bd_info struct will be passed instead=>4.4.3 信息查询命令

常用的和信息查询有关的命令有3个:bdinfo、printenv和version。

bdinfo 板子信息=> bdinfoarch_number = 0x00000000boot_params = 0x80000100DRAM bank = 0x00000000-> start = 0x80000000-> size = 0x20000000eth0name = FEC1ethaddr = (not set)current eth = FEC1ip_addr = baudrate = 115200 bpsTLB addr = 0x9FFF0000relocaddr = 0x9FF47000reloc off = 0x18747000irq_sp = 0x9EF44EA0sp start = 0x9EF44E90FB base = 0x00000000=>

从打印信息可以得出DRAM的「起始地址和大小、启动参数保存起始地址、波特率、sp(堆栈指针)起始地址」等信息.

printenv 打印环境变量=> printenvbaudrate=115200board_name=EVKboard_rev=14X14boot_fdt=trybootcmd=run findfdt;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; thn run mmcboot; else run netboot; fi; fi; else run netboot; fibootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr};bootdelay=3bootscript=echo Running bootscript from mmc ...; sourceconsole=ttymxc0ethact=FEC1ethprime=FECfdt_addr=0x83000000fdt_file=undefinedfdt_high=0xfffffffffindfdt=if test $fdt_file = undefined; then if test $board_name = EVK && test $board_rev = 9X9; then setenv fdt_file imx6ull-9x9-evk.dtb; fi; if tst $board_name = EVK && test $board_rev = 14X14; then setenv fdt_file imx6ull-14x14-evk.dtb; fi; if test $fdt_file = undefined; then echo WARNING:Could not determine dtb to use; fi; fi;image=zImageinitrd_addr=0x83800000initrd_high=0xffffffffip_dyn=yesloadaddr=0x80800000loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber="" clk_ignore_unusedmmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}mmcautodetect=yesmmcboot=echo Booting from mmc ...; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} -${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;mmcdev=0mmcpart=1mmcroot=/dev/mmcblk0p2 rootwait rwnetargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcpnetboot=echo Booting from net ...; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${imag}; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if est ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;panel=TFT43ABscript=boot.scrEnvironment size: 2431/8188 bytes=>

这里有很多的环境变量, 比如「baudrate、 board_name、 board_rec、 boot_fdt、 bootcmd」等。比如bootdelay这个环境变量就表示 uboot 启动延时时间,默认 bootdelay=3,也就默认延时 3秒。前面说的 3 秒倒计时就是由 bootdelay 定义的。另外uboot中的环境变量都是字符串。

version 版本信息=> versionU-Boot 2016.03 (Jul 11 2021 - 15:22:25 +0800)arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2017.01) 4.9.4GNU ld (Linaro_Binutils-2017.01) 2.24.0.20141017 Linaro 2014_11-3-git=>

当前uboot 版本号为 2016.03,编译日期2021/7/11,编译器为arm-linux-gnueabihf-gcc。

uboot移植实践

我们介绍了如何使用NXP原厂的uboot进行编译和烧写,将uboot运行在自己的开发板上。NXP原厂的uboot,直接烧录到我的开发板中,LCD的驱动是不正常的,需要进行修改。本篇我们就来继续研究uboot,「使得uboot能匹配我们自己的开发板」。

修改uboot以匹配开发板的方式有两种,一种是在NXP原厂开发板「i.MX 6ULL EVK」的文件上进行修改,另一种仿造NXP的开发板文件,添加自己的开发板文件。

为了能更多的了解uboot,我们使用代码改动较大的第二种方式进行uboot的移植。

在修改uboot之前,先来看一下uboot的源码结构。

1 uboot源码结构分析

uboot的源码如下,这里是源码编译后的结果,包含编译后的文件。

这里文件的含义如下:

2 uboot移植实践2.1 添加开发板配置文件

首先是「创建自己开发板的配置文件」,该文件可参考原厂开发板的配置文件,在configs文件夹下,将原来的默认配置文件mx6ull_14x14_evk_emmc_defconfig复制一份,并重命名为mx6ull_myboard_defconfig,该文件即用于作为自己开发板的配置文件。

然后进行「内容修改」,将原始内容:

CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ullevk/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"CONFIG_ARM=yCONFIG_ARCH_MX6=yCONFIG_TARGET_MX6ULL_14X14_EVK=yCONFIG_CMD_GPIO=y

修改为:

2.2 添加开发板对应的头文件

在目录 include/configs 下添加自己开发板对应的头文件,复制mx6ullevk.h,并重命名为mx6ull_myboard.h,将文件中的

#ifndef __MX6ULLEVK_CONFIG_H #define __MX6ULLEVK_CONFIG_H

修改为:

该文件里面有很多宏定义,这些宏定义基本用于配置uboot,如果我们自己要想使能或者禁止uboot的某些功能,那就要在这里面修改。

在ubuntu中,可以安装VS Code软件来辅助查看代码,在ubuntu中安装vscode,需要先下载deb格式的安装包,然后使用类似如下的指令即可进行安装:

sudo dpkg -i code_1.58.0-1625728071_amd64.deb

安装完之后,我们可以将图标添加到ubuntu桌面上,ubuntu安装的所有软件图标都在目录/usr/share/applications中,找到 Visual Studio Code 的图标,然后点击鼠标右键,选择复制到->桌面即可。

❞2.3 添加开发板对应的板级文件夹

uboot中每个板子都有一个对应的文件夹来存放板级文件(如开发板上外设驱动文件等)。NXP的I.MX系列芯片的所有板级文件夹都存放在 board/freescale/目录下,在这个目录下有个名为mx6ullevk的文件夹,原厂开发板的板级文件夹。

复制 mx6ullevk,将其重命名为mx6ull_myboard,进入mx6ull_myboard目录中, 将其中的mx6ullevk.c文件重命名为mx6ull_myboard.c。

2.3.1 修改Makefile文件

首先是修改 board/freescale/mx6ull_myboard 目录下的Makefile文件

将原始内容:

# (C) Copyright 2015 Freescale Semiconductor, Inc.## SPDX-License-Identifier: GPL-2.0+#obj-y := mx6ullevk.oextra-$(CONFIG_USE_PLUGIN) := plugin.bin$(obj)/plugin.bin: $(obj)/plugin.o $(OBJCOPY) -O binary --gap-fill 0xff $

这是因为之前有将环境变量保存到EMMC中,uboot启动以后会先从EMMC中读取环境变量,如果EMMC中没有环境变量的话才会使用 mx6ull_alientek_emmc.h 中的默认环境变量。

如果EMMC中的环境变量panel不等于GT911,那么LCD显示肯定不正常,我们只需要在uboot中修改panel的值为GT911即可,在uboot的命令模式下输入如下命令:

setenv panel GT911 saveenv

上述命令修改环境变量panel为GT911并保存后,按下复位键重启uboot,此时 LCD 驱动就工作正常了。

4 网络测试

I.MX6ULL内部有个以太网MAC外设,也就是ENET,需要外接一个PHY芯片来实现网络通信功能,也就是「内部MAC+外部PHY芯片」的方案。I.MX6ULL有两个网络接口ENET1和ENET2,野火的开发板提供了这两个网络接口,其中ENET1和ENET2都使用是和原厂开发板一样的KSZ8081作为PHY芯片。

因此,网络驱动部分的uboot不需要修改,下面就只是来测试一下网路功能。

4.1 连接网线并查看启动情况

首先将开发板通过网线连接到局域网的路由器中(自己的电脑也要在同一个局域网,这样ubuntu虚拟机则也在同一个局域网)。

然后启动uboot,串口查看相关的打印信息,如下图,可以看到网络端口的FEC1(注意是uboot程序中默认设置的,不是因为网线插在了左边就自动识别FEC1),但是提示网络地址未设置。

4.2 设置网络参数

下面就来设置一下,首先是设置开发板的IP,在设置之前,先借助Windows电脑的cmd的ping+ip指令来测试某个IP是否被使用,如我的192.168.5.102未被使用,就可以设为开发板的IP。

除了设置开发板的IP,还要设置一些其它的网络参数,具体如下:

setenv ipaddr 192.168.5.102//开发板 IP 地址setenv ethaddr 00:04:9f:04:d2:35//开发板网卡 MAC 地址setenv gatewayip 192.168.5.1//开发板默认网关setenv netmask 255.255.255.0//开发板子网掩码setenv serverip 192.168.5.101//服务器地址,也就是 Ubuntu 地址saveenv //保存环境变量

开发板的MAC地址是一个长度为48位(6个字节)的地址,每个字节间通过冒号间隔,理论上只要局域网内各网络设备不冲突,该地址可任意设置。

局域网的默认网关和子网掩码需要根据自己的实际情况设置(不知道是多少的,可以借助Windows电脑的cmd中的ipconfig指令来查看)

服务器的地址就是ubuntu虚拟机的地址(可以通过linux的ifconfig指令来查看)

4.3 测试另一个网口

打开 include/configs/mx6ull_alientek_emmc.h ,将CONFIG_FEC_ENET_DEV修改为 0, 重新编译uboot并烧写到SD卡中。

将网线连接到开发板右边的网口上,按照之前的测试方法再次测试:

5 uboot启动Linux内核测试

uboot的最终目的就是启动Linux内核,所以需要通过启动Linux内核来判断uboot移植是否成功。

启动Linux内核。我们测试两种启动Linux内核的方法:

从EMMC启动从网络启动

「从EMMC启动」也就是将编译出来的「Linux镜像文件zImage」和「设备树文件」保存在EMMC中,uboot从EMMC中读这两个文件并启动。由于我们板子的EMMC中可能还没有linux镜像文件和设备树文件,所以先不测试这种方法。

「从网络启动」,是指将linux镜像文件和根文件系统都放到Ubuntu下某个指定的文件夹中,然后通过nfs或者tftp等传输方式将系统文件(zImage和设备树文件)从Ubuntu中直接下载到开发板的内存中,EMMC中则不需要有系统文件。这种方式的作用就是方便调试,免去将代码固化到开发板的过程。当然,当开发板掉电,内存的系统文件就没了。

下面就来通过网络调试的方法来测试uboot是否能正常启动Linux内核。

「在测试之前,先来介绍一下在ubuntu虚拟机上如何搭建tftp来传输文件」。

5.1 tftp服务搭建

Ubuntu上搭建TFTP服务器,需要安装tftp-hpa和tftpd-hpa,命令如下:

sudo apt-get install tftp-hpa tftpd-hpa sudo apt-get install xinetd

TFTP也需要一个文件夹来存放文件,在用户目录下新建一个目录,示例命令如下:

mkdir /home/xxpcb/myTest/tftpdirchmod 777 /home/xxpcb/myTest/tftpdir

最后配置 tftp, 安装完成以后,新建文件/etc/xinetd.d/tftp, 如果没有/etc/xinetd.d 目录的话自行创建,然后在里面输入如下内容:

server tftp { socket_type = dgram protocol = udp wait = yes user = root server = /usr/sbin/in.tftpd server_args = -s /home/xxpcb/myTest/tftpdir/ disable = no per_source = 11 cps = 100 2 flags = IPv4 }

完了以后启动tftp服务,命令如下:

sudo service tftpd-hpa start

打开/etc/default/tftpd-hpa文件,将其修改为如下所示内容:

# /etc/default/tftpd-hpa TFTP_USERNAME="tftp"TFTP_DIRECTORY="/home/xxpcb/myTest/tftpdir"TFTP_ADDRESS=":69"TFTP_OPTIONS="-l -c -s"

TFTP_DIRECTORY就是我们上面创建的tftp文件夹目录,以后我们就将所有需要通过TFTP传输的文件都放到这个文件夹里面,并且要给予这些文件相应的权限。

最后输入如下命令, 重启 tftp 服务器:

sudo service tftpd-hpa restart

至此,tftp服务器已经搭建好了,可以先来测试一下功能是否正常。

5.2 tftp文件传输测试

测试tftp功能是否正常,主要分为两步:

首先是「将某个zImage镜像文件拷贝到ubuntu虚拟机的tftpboot文件夹中」,并且给予 zImage 相应777的权限。

然后是「通过开发板uboot的串口交互指令将文件从ubuntu传输到开发板的内存」。

uboot串口交互指令中的「tftp命令格式」如下:

tftpboot [loadAddress] [[hostIPaddr:]bootfilename]

loadAddress是文件在DRAM中的存放地址,[[hostIPaddr:]bootfilename]是要从Ubuntu中下载的文件。

tftp传输文件,不需要输入文件在Ubuntu中的完整路径,只需要输入文件名即可。

比如我们现在「将tftpboot文件夹里面的zImage文件下载到开发板DRAM的0X80800000地址处」,命令如下:

tftp 80800000 zImage

注:此次测试时,我的ubuntu虚拟机(作为tftp服务器)的IP变了,所以我又重新设置了ubuntu的IP

❞5.3 测试从网络启动Linux

设置环境变量

这两个环境变量的具体含义先不展开讨论。

setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-evk-emmc.dtb; bootz 80800000 - 83000000'saveenv

通过tftp将zImage和设备树下载到板子的RAM中

就是通过网路的方式(tftp)将系统文件下载到板子的内存中,这里使用的「野火提供的yocto的zImage和dtb文件」,将两个文件辅助到ubuntu的tftp服务器目录,依次输入如下指令:

tftp 80800000 zImagetftp 83000000 imx6ull-14x14-evk-emmc.dtb启动内核bootz 80800000 - 83000000

可以看到「Starting kernel ...」 的字样,表示内核已经启动。

再看看下板子,已经有启动画面了:

在过一会儿,会出现系统的图形界面,只是现在还不能操作,触摸没反应。

至此,uboot的移植基本完成,可以启动Linux内核。启动内核之后,uboot的使命就完成了。

Kernel移植1 Linux内核简介

官网:https://www.kernel.org/

NXP 会从linux内核官网下载某个版本,然后将其移植到自己的 CPU上,测试成功后就会将其开放给NXP的CPU开发者。开发者下载 NXP 提供的 Linux 内核,然后将其移植到自己的产品上。

本文我们就使用NXP提供的Linux源码,文件名为:linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

2 Linux内核编译

编译内核之前需要先在ubuntu上安装lzop库,另外,图形化配置工具还需要ncurses库支持,安装命令为:

sudo apt-get install lzopsudo apt-get install build-essential sudo apt-get install libncurses5-dev

在Ubuntu中新建一个文件夹,然后将linux内核压缩包拷贝到文件夹中并解压,解压命令为:

tar -vxf linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

解压完成后

进入该文件夹,新建一个build.sh脚本文件来编译,脚本中的内容如下:

#!/bin/sh make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_mfg_defconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j8

给予该脚本可执行权限,然后运行,编译的时候会弹出Linux图形配置界面, 这里不需要做任何的配置, 直接按两下ESC键退出图形界面

之后会自动开始编译Linux内核。

编译完成以后就会在arch/arm/boot这个目录下生成一个zImage文件,该文件就是要用的Linux镜像文件。另外也会在arch/arm/boot/dts下生成很多.dtb 文件,这些.dtb 就是设备树文件。

vmlinux 、Image ,zImage 、uImage  的区别

vmlinux是ELF格式的文件,是编译出来的最原始的内核文件,编译出来 差不多有16MB,是未压缩的。在实际中我们不会使用vmlinux,而是使用zImage或uImage这样的 Linux 内核镜像文件。

Image是Linux内核镜像文件,但是Image仅包含可执行的二进制数据。Image就是使用objcopy取消掉vmlinux中的一些其他信息,比如符号表什么的。但是 Image 是没有压缩过的,Image保存在arch/arm/boot目录下,其大小大概在12MB 。

zImage是经过gzip压缩后的Image,经过压缩以后其大小大概在6MB左右。

uImage是老版本uboot专用的镜像文件,uImag是在zImage前面加了一个长度为 64字节的“头” ,这个头信息描述了该镜像文件的类型、加载位置、生成时间、大小等信息。但是新的uboot已经支持了 zImage 启动!所以已经很少用到uImage了。

3 Linux内核源码结构

Linux内核编译过程会生成一些文件,下面来看一下编译后的内核源码结构,可以看出多出了一些编译文件

具体描述如下:

arch目录

这个目录是和架构有关的目录,比如arm、arm64、avr32、x86等等架构。每种架构都对应一个目录,在这些目录中又有很多子目录,比如boot、common、configs等等。

block目录 block是Linux下块设备目录, 像SD卡、EMMC、NAND、硬盘等存储设备就属于块设备,block目录中存放着管理块设备的相关文件。

crypto目录 crypto目录里面存放着加密文件,比如常见的crc、crc32、md4、md5、hash等加密算法。

Documentation目录 此目录里面存放着Linux相关的文档,如果要想了解Linux某个功能模块或驱动架构的功能,就可以在Documentation目录中查找有没有对应的文档。

drivers目录 驱动目录文件,此目录根据驱动类型的不同,分门别类进行整理,比如drivers/i2c就是I2C相关驱动目录,drivers/gpio就是GPIO相关的驱动目录,这是我们学习的重点。

firmware 目录 此目录用于存放固件。

fs目录 此目录存放文件系统,比如fs/ext2、fs/ext4、fs/f2fs等,分别是ext2、ext4 和 f2fs等文件系统。

4 Linux内核启动测试

将编译出来的zImage和imx6ull-14x14-evk.dtb复制到Ubuntu中的tftp目录下,之后会通过uboot 的tftp命令将其下载到开发板中。

在测试之前确保uboot中的环境变量bootargs内容如下(使用print指令查看):

console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw

如果不是的话,可以使用如下指令设置一下:

setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'saveenv

然后可以测试了,启动开发板,串口中进入uboot命令行模式,然后输入如下命令将zImage和imx6ull-14x14-evk.dtb下载到开发板中并启动:

tftp 80800000 zImage tftp 83000000 imx6ull-14x14-evk.dtb bootz 80800000 - 83000000

可以看到内核启动了

最后到了系统登录处,说明Linux系统正常启动了(这次LCD上没有了野火的图形界面,可能是某些固件不匹配吧,先忽略)

4.1 根文件系统缺失的错误

Linux内核启动以后是需要根文件系统的,根文件系统存在哪里是由uboot的bootargs环境变量指定, bootargs会传递给Linux内核作为命令行参数 。比如之前设置的root=/dev/mmcblk1p2,也就是说根文件系统存储在/dev/mmcblk1p2中,即EMMC的分区2中。

因为上一篇的测试时,EMMC的分区2中烧写好了根文件系统,所以设置root=/dev/mmcblk1p2,并且内核正常启动。如果我们不设置根文件系统路径,或者说根文件系统路径设置错误的话会出现什么问题?

我们将uboot中的bootargs环境变量改为“console=ttymxc0,115200” ,也就是不填写root的内容了,命令如下:

setenv bootargs 'console=ttymxc0,115200'saveenv

修改完成以后重新从网络启动,可以看到也是先启动了内核:

但启动以后会有类似如下的错误:

最后会有下面这一行:

Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

提示内核崩溃,因为VFS(虚拟文件系统)不能挂载根文件系统,目录不存在。即使目录存在,如果根文件系统目录里面是空的依旧会提示内核崩溃。

5 Linux中添加自己的开发板

编译NXP官方I.MX6ULL EVK开发板对应的Linux内核,发现其可以在野火的EMMC版本开发板启动。为了进一步了解Linux内核,我们可以参考官方开发板的设置,在Linux内核中添加自己的开发板。

5.1 添加开发板默认配置文件

将arch/arm/configs目录下的imx_v7_mfg_defconfig重新复制一份 , 命名为自己开发板,如imx_myboard_defconfig。

5.2 添加开发板对应的设备树文件

进入arch/arm/boot/dts目录中,复制一份imx6ull-14x14-evk.dts,然后将其重命名为imx6ull-myboard.dts。

然后还需要修改文件arch/arm/boot/dts/Makefile,找到 dtb-$(CONFIG_SOC_IMX6ULL)配置项,在此配置项中加入“imx6ull-myboard.dtb” :

这样编译Linux的时候就可以从imx6ull-myboard.dts 编译出 imx6ull-myboard.dtb 文件了。

总结一下以上的修改主要包括:

主要就是对文件复制一份并重命名,唯一修改的是Makefile文件。

5.3 添加新的编译脚本

新建一个build_myboard.sh,写入如下内容:

#!/bin/sh make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_myboard_defconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j8

编译出zImage(arch/arm/boot目录)和imx6ull-myboard.dtb (arch/arm/boot/dts目录)后再次进行Linux启动测试,可以到登录提示,说明Linux内核启动成功。

根文件系统构建

本篇进行根文件系统的构建,这是Linux移植三大组成部分的最后一步,根文件系统构建好后,就构成了一个基础的、可以运行的嵌入式Linux最小系统。

1 根文件系统简介

Linux的根文件系统一般也叫做 rootfs,Linux的根文件系统更像是一个文件夹或者叫做目录,在这个目录里面会有很多的子目录。根目录下和子目录中会有很多的文件,这些文件是Linux运行所必须的,比如库、常用的软件和命令、设备文件、配置文件等等。

根文件系统的这个“根”字就说明了这个文件系统的重要性,它是其他文件系统的根,没有这个“根” ,其他的文件系统或者软件就别想工作。比如我们常用的 ls、mv、ifconfig 等命令其实就是一个个小软件,只是这些软件没有图形界面,而且需要输入命令来运行。这些小软件就保存在根文件系统中。

在构建根文件系统之前,先来看一下根文件系统里面都有些什么内容,根文件系统的目录名字为‘/’ ,就是一个斜杠:

根文件系统的各个文件夹的作用如下:

目录描述/bin此目录下存放着系统需要的可执行文件,一般都是一些命令,比如 ls、mv 等命令/devdev 是 device 的缩写,所以此目录下的文件都是和设备有关的。在Linux下一切皆文件,即使是硬件设备,也是以文件的形式存在的,比如/dev/ttymxc0就表示串口0/etc此目录下存放着各种配置文件/liblib是library的简称,也就是库的意思,因此此目录下存放着Linux所必须的库文件/mnt临时挂载目录,一般是空目录,可以在此目录下创建空的子目录,比如/mnt/sd、/mnt/usb,这样就可以将SD卡或者U盘挂载到/mnt/sd 或者/mnt/usb 目录中/proc此目录一般是空的,当Linux系统启动以后会将此目录作为proc文件系统的挂载点,proc是个虚拟文件系统,没有实际的存储设备。proc里面的文件都是临时存在的,一般用来存储系统运行信息文件/usr注意,usr不是user的缩写,而是Unix Software Resource的缩写,即Unix操作系统软件资源目录。Linux 一般被称为类Unix操作系统。既然是软件资源目录,因此/usr 目录下也存放着很多软件,一般系统安装完成以后此目录占用的空间最多/var此目录存放一些可以改变的数据/sbin此目录页用户存放一些可执行文件, 但是此目录下的文件或者说命令只有管理员才能使用,主要用于系统管理/sys系统启动以后此目录作为 sysfs 文件系统的挂载点,sysfs是一个类似于 proc文件系统的特殊文件系统,sysfs也是基于RAM的文件系统,也就是说它也没有实际的存储设备。此目录是系统设备管理的重要目录/opt可选的文件、软件存放区,由用户选择将哪些文件或软件放到此目录中2 BusyBox构建根文件系统2.1 BusyBox简介

BusyBox是一个集成了大量的Linux命令(如ls、mv、ifconfig 等命令)和工具的软件。借助BusyBox,进行配置和编译,就可以方便的构建一个嵌入Linux平台所需要的根文件系统。

课程BusyBox官网https://busybox.net/下载源码,如下图。

左侧的“Get BusyBox”栏有一行“Download Source” ,点击“Download Source”即可打开 BusyBox 的下载页。

目前最新的 BusyBox 版本是1.33.1,但这里使用正点原子提供的1.29.0版本的BusyBox(busybox-1.29.0.tar.bz2)

2.2 搭建NFS服务

一般在Linux驱动开发的时候都是通过NFS挂载根文件系统的,当调试好之后再将根文件系统烧写到 EMMC或者NAND中,因此需要先在ubuntu虚拟机中构建NFS服务:

sudo apt-get install nfs-kernel-server rpcbind

等待安装完成,在合适的地方新建一个名为“nfs”的文件夹,供NFS服务器使用。

如我的创建目录为:/home/xxpcb/myTest/nfs

在使用NFS之前,还需要先配置NFS,修改配置文件/etc/exports,在后面添加如下所示内容:

/home/xxpcb/myTest/nfs *(rw,sync,no_root_squash)

最后重启NFS服务即可:

sudo /etc/init.d/nfs-kernel-server restart

正常情况会出现如下图,表示设置成功:

注:我第一次设置时,文件路径中的一个大小写字母搞错了,导致重启NFS时提示失败(如下图),所以在设置时要注意细节!

2.3 修改配置BusyBox

在nfs服务器目录中创建一个名为rootfs的子目录,用来存放我们的根文件系统。

将busybox-1.29.0.tar.bz2发送到Ubuntu中的合适位置(我存放在 /home/xxpcb/myTest/imx6ull/dts)并解压:

tar -vxjf busybox-1.29.0.tar.bz2

解压后的文件如下:

2.3.1 修改Makefile添加编译器

注:这一步可以不修改,这里修改Makefile的目的是为了在编译时,可以不用在指定编译器的架构,从而可以缩短手动输入指令的长度。但我此次测试时,修改Makefile后,输入make指令的命令进行编译时,不指定编译器,还是会提示编译器找不到之类的问题。所以,此次的测试,我就没有修改这个Makefile。

如果坚持要修改Makefile,就是修改如下的地方,指定编译器与架构(本篇进行实验时没有修改)。

2.3.2 busybox中文字符支持

现在如果直接编译busybox的,在使用串口工具的时候是不支持中文显示的,中文字符会显示为“?” 。可以通过busybox源码,来取消 busybox对中文显示的限制。

打开文件busybox-1.29.0/libbb/printable_string.c,找到函数printable_string,吧某些程序注释掉,修改后的函数内容如下:

主要就是禁止字符大于0X7F以后 break 和输出‘?’

接着打开文件busybox-1.29.0/libbb/unicode.c,修改如下内容:

2.3.3 配置busybox

有以下几种配置选项:

defconfig:缺省配置,也就是默认配置选项allyesconfi:全选配置,也就是选中 busybox 的所有功能allnoconfig:最小配置

一般使用默认配置即可,因此使用如下命令先使用默认配置来配置一下 busybox:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig

busybox也支持图形化配置,通过图形化配置我们可以进一步选择自己想要的功能,输入如下命令打开图形化配置界面:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig(1) 设置Settings -> Build static binary (no shared libs)

选项“Build static binary (no shared libs)”用来决定是静态编译还是动态编译,静态编译的话就不需要库文件,但是编译出来的库会很大。动态编译的话要求根文件系统中有库文件,但是编译出来的 busybox 会小很多。这里我们不使用静态编译,所以保持默认不选即可。

(2) 设置Settings -> vi-style line editing commands

这个要勾选,通过按键“y”实现勾选,使得方括号内出现星号

(3) 配置Linux Module Utilities -> Simplified modutils

默认会选中“Simplified modutils” ,这里我们要取消勾选!使用键盘上的“n”键取消方括号中的星号。

(4) 配置Linux System Utilities  -> mdev (16 kb)

确保下面的全部选中,默认都是选中的

(5) 设置Settings -> Support Unicode

要将默认没有勾选的Check $LC_ALL项选中!

最后按两下ESC退出设置,并选择YES保持存。

2.4 编译busybox构建根文件系统

输入如下指令进行编译:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- install CONFIG_PREFIX=/home/xxpcb/myTest/nfs/rootfs

编译完成以后, busybox的所有工具和文件就会被安装到rootfs目录中,如下图:

rootfs目录下有bin、sbin和usr三个目录,以及linuxrc文件。Linux内核linit进程最后会查找用户空间的init程序,找到以后就会运行这个用户空间的init程序,从而切换到用户态。如果bootargs设置init=/linuxrc,那么linuxrc就是可以作为用户空间的init程序。

2.5 向根文件系统添加lib库

busybox编译完成后,此时的根文件系统还不能使用, 还需要一些其他的文件。

2.5.1 向rootfs/lib中添加

上面的busybox使用的是动态库编译,所以还需要向根文件系统中添加动态库。

先在rootfs中创建一个名为“lib”的文件夹。lib库文件从交叉编译器中获取,之前搭建交叉编译环境的时候将交叉编译器存放到了“/usr/local/arm/”目录中,进入对应的目录:

cd /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib

此目录下有很多的so和.a 文件,这些就是库文件,将此目录下所有的so和.a文件都拷贝到 rootfs/lib 目录中:

cp *so* *.a /home/xxpcb/myTest/nfs/rootfs/lib/ -d

后面的“-d”表示拷贝符号链接,这里有个比较特殊的库文件:ld-linux-armhf.so.3,此库文件也是个符号链接,相当于 Windows 下的快捷方式。会链接到库 ld-2.19-2014.08-1-git.so 上,输入命令如下指令查看此文件详细信息:

ls ld-linux-armhf.so.3 -l

ld-linux-armhf.so.3 后面有个“->” ,表示其是个软连接文件,链接到文件ld-2.19-2014.08-1-git.so,因为其是一个“快捷方式” ,因此大小只有 24B。但是,ld-linux-armhf.so.3不能作为符号链接,否则的话在根文件系统中执行程序无法执行!所以我们需要重新复制ld-linux- armhf.so.3,替换掉这个软链接。

先删除这个软连接文件:

rm ld-linux-armhf.so.3

然后重新进入到 /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm- linux-gnueabihf/libc/lib 目录中,重新拷贝ld-linux-armhf.so.3,命令如下:

cp ld-linux-armhf.so.3 /home/xxpcb/myTest/nfs/rootfs/lib/

拷贝完成以后再到 rootfs/lib 目录下查看ld-linux-armhf.so.3文件详细信息,此时ld-linux-armhf.so.3 已经不是软连接了,而是实实在在的一个库文件,而且文件大小为 724392B。

继续进入如下目录中:

cd /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib

此目录下也有很多的的so和.a 库文件,我们将其也拷贝到 rootfs/lib 目录中,命令如下:

cp *so* *.a /home/xxpcb/myTest/nfs/rootfs/lib/ -d

rootfs/lib 目录的库文件就这些了,完成以后的rootfs/lib目录如图:

2.5.2 向rootfs/usr/lib中添加

在rootfs/usr目录下创建一个名为lib的目录, 将如下目录中的库文件拷贝到rootfs/usr/lib目录下:

/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib

将此目录下的so和.a 库文件都拷贝到rootfs/usr/lib目录中:

cp *so* *.a /home/xxpcb/myTest/nfs/rootfs/usr/lib/ -d

完成以后的rootfs/usr/lib目录为:

至此,根文件系统的库文件就全部添加好了,可以在rootfs目录下使用“du”命令来查看一下/lib和/usr/lib 这两个目录的大小:

du ./lib ./usr/lib/ -sh2.6 创建其他文件夹

在根文件系统中创建其他文件夹,如 dev、proc、mnt、sys、tmp 和 root 等,创建完后的效果:

3 根文件系统初步测试3.1 bootargs环境变量设置

使用NFS挂载的方式来测试上面创建好的根文件系统rootfs。

uboot里面的bootargs环境变量会设置root的值,需要将root的值改为NFS挂载,设置格式如为:

root=/dev/nfs nfsroot=[:][,] ip=:::::::::服务器IP,存放根文件系统的Ubuntu的IP地址,比如我的192.168.5.105。 :根文件系统的存放路径,比如我的就是/home/xxpcb/myTest/nfs/rootfs。:NFS 的其他可选选项,一般不设置。 :客户端IP ,开发板的IP地址,Linux内核启动以后就会使用此IP地址来配置开发板。我的为92.168.5.102。 :网关地址,我的就是 192.168.5.1。:子网掩码,我的就是 255.255.255.0。:客户机的名字,一般不设置,此值可以空着。 :设备名,也就是网卡名,一般是 eth0,eth1….,正点原子与野火的开发板均为ENET2为eth0,ENET1为eth1。这里我们使用ENET2,所以网卡名就是 eth0。 :自动配置,一般不使用,所以设置为 off。 :DNS0 服务器 IP 地址,不使用。 :DNS1 服务器 IP 地址,不使用。

根据上面的格式bootargs环境变量的root值如下:

root=/dev/nfs nfsroot=192.168.5.105:/home/xxpcb/myTest/nfs/rootfs,proto=tcp rw ip=192.168.5.102:192.168.5.105:192.168.5.1:255.255.255.0::eth1:off

启动开发板,串口连接开发板,进入uboot命令行模式,然后设置bootargs环境变量,命令如下:

setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.5.105:/home/xxpcb/myTest/nfs/rootfs,proto=tcp rw ip=192.168.5.102:192.168.5.105:192.168.5.1:255.255.255.0::eth1:off'saveenv

设置好以后使用“boot”命令启动Linux内核

Linux内核的启动还是按照上一篇介绍的,使用tftp将zImage和设备树传输到开发板中运行。

这里注意一下,因为此次测试,我将zImage和dtb文件移入了tftp目录中的nxp文件夹中,所以传输指令需要修改一下:

setenv bootcmd 'tftp 80800000 nxp/zImage; tftp 83000000 nxp/imx6ull-myboard.dtb; bootz 80800000 - 83000000'saveenv

然后就可以使用boot命令来进行tftp传输了。

3.2 NFS挂载错误与解决方法3.2.1 错误提示

在使用boot命令来进行tftp传输了,启动内核时,出现了NFS根文件系统不能挂载的错误:

VFS: Unable to mount root fs  via NFS, trying floppy.

VFS: Cannot open root device "nfs" or unknown-block(2,0): error -6

3.2.2 无效的解决方法

先是尝试了多种方法,都不能解决问题,这些无效的方法包括:

尝试修改配置,将Linux System Utilities  ->Support mounting NFS file选中(无效)尝试将nfs目录的下的rootfs文件夹赋予777的权限(无效)尝试换用其它的串口软件(SecureCRT)来操作(无效)3.2.3 有效的解决方法

最后,参考这篇博文:https://blog.csdn.net/InFoport/article/details/90317697

通过在bootargs添加中添加nfsvers=4,这个选项,就可以正常挂载nfs的文件系统了:

setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.5.105:/home/xxpcb/myTest/nfs/rootfs,proto=tcp,nfsvers=4 rw ip=192.168.5.102:192.168.5.105:192.168.5.1:255.255.255.0::eth1:off'

注:无效方法中的Linux System Utilities  ->Support mounting NFS file,因开启后也不起作用,后续测试就将其改为默认的不勾选。

3.3 文件系统使用测试

按下回车键,就进入了文件系统,使用ls命令就可以看到了系统文件。

再使用touch命令来新建一个中文名称的文件,也是OK的。

4 总结

本篇使用BusyBox来构建根文件系统,并通过NFS网络调试的方式实现根文件系统挂载测试,实测时解决了NFS根文件系统不能挂载的问题,最终根文件系统基本功能测试正常。

关注,回复【1024】海量Linux资料赠送 精彩文章合集linux入门C语言Linux驱动ARM计算机网络粉丝问答所有原创


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3